#include "precompiled.h"
#pragma hdrstop

#include <ctype.h>
#include <cstdio>
#include <memory.h>						// needed for memcmp

namespace inf {

    infSys*         sys = NULL;
    Common*         common = NULL;
    FileSystem*     fs = NULL;

    void init( void ) {
        inf::endian_init();
        inf::math::init();
        inf::mem_init();
    }

    void shutdown( void ) {
        inf::mem_shutdown();
    }

    void printf( const char *fmt, ... ) {
        va_list		argptr;
        char		text[INF_MAX_STRING_CHARS] = {0};

        va_start( argptr, fmt );
        vsnPrintf( text, INF_MAX_STRING_CHARS, fmt, argptr );
        va_end( argptr );
        std::printf(text);
    }

    void error( const char *fmt, ... ) {
        va_list		argptr;
        char		text[INF_MAX_STRING_CHARS] = "error: ";

        va_start( argptr, fmt );
        vsnPrintf( text+7, INF_MAX_STRING_CHARS - 7, fmt, argptr );
        va_end( argptr );

        printf(text);
    }

    void warning( const char *fmt, ... ) {
        va_list		argptr;
        char		text[INF_MAX_STRING_CHARS] = "warning: ";

        va_start( argptr, fmt );
        vsnPrintf( text+9, INF_MAX_STRING_CHARS - 9, fmt, argptr );
        va_end( argptr );

        printf(text);
    }

    void fatal_error( const char* fmt, ...) {
        va_list		argptr;
        char		text[INF_MAX_STRING_CHARS];

        va_start( argptr, fmt );
        vsnPrintf( text, INF_MAX_STRING_CHARS, fmt, argptr );
        va_end( argptr );

        sys->MessageBoxChar("fatal_error", text, MB_FATALERROR);
    }

    void printf( const wchar_t *fmt, ... ) {
        va_list		argptr;
        wchar_t		text[INF_MAX_STRING_CHARS] = {0};

        va_start( argptr, fmt );
        vsnPrintf( text, INF_MAX_STRING_CHARS, fmt, argptr );
        va_end( argptr );
        ::wprintf_s(text);
    }

    void error( const wchar_t *fmt, ... ) {
        va_list		argptr;
        wchar_t		text[INF_MAX_STRING_CHARS] = L"error: ";

        va_start( argptr, fmt );
        vsnPrintf( text+7, INF_MAX_STRING_CHARS - 7, fmt, argptr );
        va_end( argptr );

        wprintf_s(text);
    }

    void warning( const wchar_t *fmt, ... ) {
        va_list		argptr;
        wchar_t		text[INF_MAX_STRING_CHARS] = L"warning: ";

        va_start( argptr, fmt );
        vsnPrintf( text+9, INF_MAX_STRING_CHARS - 9, fmt, argptr );
        va_end( argptr );

        wprintf_s(text);
    }

    void fatal_error( const wchar_t* fmt, ...) {
        va_list		argptr;
        wchar_t		text[INF_MAX_STRING_CHARS];

        va_start( argptr, fmt );
        vsnPrintf( text, INF_MAX_STRING_CHARS, fmt, argptr );
        va_end( argptr );

        sys->MessageBoxWChar(L"fatal_error", text, MB_FATALERROR);
    }

    /*
    ============
    va

    does a varargs printf into a temp buffer
    NOTE: not thread safe
    ============
    */
    char *va( const char *fmt, ... ) {
        va_list argptr;
        static int index = 0;
        static char string[4][16384];	// in case called by nested functions
        char *buf;

        buf = string[index];
        index = (index + 1) & 3;

        va_start( argptr, fmt );
        vsprintf( buf, fmt, argptr );
        va_end( argptr );

        return buf;
    }

    char *vva( char *buf, const char *fmt, ... ) {
        va_list argptr;

        va_start( argptr, fmt );
        vsprintf( buf, fmt, argptr );
        va_end( argptr );

        return buf;
    }

    /*
    =================
    va_floatstring
    =================
    */
    char* va_floatstring( const char *fmt, ... ) {
        va_list argPtr;
        static int bufferIndex = 0;
        static char string[4][16384];	// in case called by nested functions
        char *buf;

        buf = string[bufferIndex];
        bufferIndex = (bufferIndex + 1) & 3;

        long i;
        unsigned long u;
        double f;
        char *str;
        int index;
        Str tmp, format;

        index = 0;

        va_start( argPtr, fmt );
        while( *fmt ) {
            switch( *fmt ) {
            case '%':
                format = "";
                format += *fmt++;
                while ( (*fmt >= '0' && *fmt <= '9') ||
                    *fmt == '.' || *fmt == '-' || *fmt == '+' || *fmt == '#') {
                        format += *fmt++;
                }
                format += *fmt;
                switch( *fmt ) {
            case 'f':
            case 'e':
            case 'E':
            case 'g':
            case 'G':
                f = va_arg( argPtr, double );
                if ( format.length() <= 2 ) {
                    // high precision floating point number without trailing zeros
                    sprintf( (char*) tmp.c_str(), "%1.10f", f );
                    tmp.strip_trailing( '0' );
                    tmp.strip_trailing( '.' );
                    index += sprintf( buf+index, "%s", tmp.c_str() );
                }
                else {
                    index += sprintf( buf+index, format.c_str(), f );
                }
                break;
            case 'd':
            case 'i':
                i = va_arg( argPtr, long );
                index += sprintf( buf+index, format.c_str(), i );
                break;
            case 'u':
                u = va_arg( argPtr, unsigned long );
                index += sprintf( buf+index, format.c_str(), u );
                break;
            case 'o':
                u = va_arg( argPtr, unsigned long );
                index += sprintf( buf+index, format.c_str(), u );
                break;
            case 'x':
                u = va_arg( argPtr, unsigned long );
                index += sprintf( buf+index, format.c_str(), u );
                break;
            case 'X':
                u = va_arg( argPtr, unsigned long );
                index += sprintf( buf+index, format.c_str(), u );
                break;
            case 'c':
                i = va_arg( argPtr, long );
                index += sprintf( buf+index, format.c_str(), (char) i );
                break;
            case 's':
                str = va_arg( argPtr, char * );
                index += sprintf( buf+index, format.c_str(), str );
                break;
            case '%':
                index += sprintf( buf+index, format.c_str() );
                break;
            default:
                printf( "FS_WriteFloatString: invalid format %s", format.c_str() );
                break;
                }
                fmt++;
                break;
            case '\\':
                fmt++;
                switch( *fmt ) {
            case 't':
                index += sprintf( buf+index, "\t" );
                break;
            case 'v':
                index += sprintf( buf+index, "\v" );
                break;
            case 'n':
                index += sprintf( buf+index, "\n" );
                break;
            case '\\':
                index += sprintf( buf+index, "\\" );
                break;
            default:
                printf( "FS_WriteFloatString: unknown escape character \'%c\'", *fmt );
                break;
                }
                fmt++;
                break;
            default:
                index += sprintf( buf+index, "%c", *fmt );
                fmt++;
                break;
            }
        }
        va_end( argPtr );

        return buf;
    }

    /*
    ============
    va (wchar_t)

    does a varargs printf into a temp buffer
    NOTE: not thread safe
    ============
    */
#define VA_BUF_LEN 16384
    wchar_t *va( const wchar_t *fmt, ... ) {
        va_list argptr;
        static int index = 0;
        static wchar_t string[4][VA_BUF_LEN];	// in case called by nested functions
        wchar_t *buf;

        buf = string[index];
        index = (index + 1) & 3;

        va_start( argptr, fmt );
        vsnPrintf( buf, VA_BUF_LEN, fmt, argptr );
        va_end( argptr );

        return buf;
    }

    int snPrintf( char *dest, int size, const char *fmt, ...) {
        int ret;
        va_list argptr;

#if (INF_COMPILER == COMPILER_MSVC)
#undef _vsnprintf
        va_start( argptr, fmt );
        ret = _vsnprintf( dest, size-1, fmt, argptr );
        va_end( argptr );
#else
#undef vsnprintf
        va_start( argptr, fmt );
        ret = vsnprintf( dest, size, fmt, argptr );
        va_end( argptr );
#endif
        dest[size-1] = inf::ConstChar<char>::StrZero;
        if ( ret < 0 || ret >= size ) {
            return -1;
        }
        return ret;
    }

    int snPrintf( wchar_t *dest, int size, const wchar_t *fmt, ...) {
      int ret;
      va_list argptr;

#if (INF_COMPILER == COMPILER_MSVC)
#undef _vsnwprintf
      va_start( argptr, fmt );
      ret = _vsnwprintf( dest, size-1, fmt, argptr );
      va_end( argptr );
#else
#undef vsnwprintf
      va_start( argptr, fmt );
      ret = vsnwprintf( dest, size, fmt, argptr );
      va_end( argptr );
#endif
      dest[size-1] = ConstChar<wchar_t>::StrZero;
      if ( ret < 0 || ret >= size ) {
        return -1;
      }
      return ret;
    }

    int vsnPrintf( char *dest, int size, const char *fmt, va_list argptr ) {
        int ret;

#if (INF_COMPILER == COMPILER_MSVC)
#undef _vsnprintf
        ret = _vsnprintf( dest, size-1, fmt, argptr );
#else
#undef vsnprintf
        ret = vsnprintf( dest, size, fmt, argptr );
#endif
        dest[size-1] = ConstChar<char>::StrZero;
        if ( ret < 0 || ret >= size ) {
            return -1;
        }
        return ret;
    }

    int vsnPrintf( wchar_t *dest, int size, const wchar_t *fmt, va_list argptr ) {
        int ret;

#if (INF_COMPILER == COMPILER_MSVC)
#undef _vsnwprintf
        ret = _vsnwprintf( dest, size-1, fmt, argptr );
#else
#undef vsnwprintf
        ret = vsnwprintf( dest, size, fmt, argptr );
#endif
        dest[size-1] = ConstChar<wchar_t>::StrZero;
        if ( ret < 0 || ret >= size ) {
            return -1;
        }
        return ret;
    }

    void* memory_copy(void* dst, const void* src, size_t sz) {
        return memcpy(dst, src, sz);
    }

    void* memory_set(void* dst, const int v, size_t sz) {
        return memset(dst,v,sz);
    }

    void* memory_move(void *dst, const void *src, size_t count) {
        return memmove(dst, src, count);
    }

    void assert_failed( const char *file, int line, const char *expression ) {
        printf( "\n\nASSERTION FAILED!\n%s(%d): '%s'\n", file, line, expression );

#if defined( _WIN32 )
        __debugbreak();
#elif defined( __linux__ )
        __asm__ __volatile__ ("int $0x03");
#elif defined( MACOS_X )
        kill( getpid(), SIGINT );
#endif
    }

    void assert_failed( const wchar_t *file, int line, const wchar_t *expression ) {
        printf( L"\n\nASSERTION FAILED!\n%s(%d): '%s'\n", file, line, expression );

#if defined( _WIN32 )
        __debugbreak();
#elif defined( __linux__ )
        __asm__ __volatile__ ("int $0x03");
#elif defined( MACOS_X )
        kill( getpid(), SIGINT );
#endif
    }

}   // ::inf

